using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace Open.Data.Persistence.SqlServer
{
    public class SqlReflectionParser: ReflectionParser  
    {
        //Property variables
        private bool _isIdentity;
        private PropertyInfo _identity;

        public bool IsIdentity
        {
            get
            {
                return _isIdentity;
            }
            set
            {
                _isIdentity = value;
            }
        }

        public PropertyInfo Identity
        {
            get
            {
                return _identity;
            }
            set
            {
                _identity = value;
            }
        }

        //Methods
        public override void Reset()
        {
            base.Reset();

            _isIdentity = false;
            _identity = null;
        }

        //Do not use the ReflectionCache as this can cause loops and stack overflows
        public override void Validate(Type type)
        {
            base.Validate(type);

            PropertyInfo[] propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.GetProperty);

            //Check key properties have to have length if a string
            foreach (PropertyInfo property in propertyInfo)
            {
                //Check indexed strings without length
                if (property.PropertyType == typeof(string))
                {
                    bool hasLength = false;
                    bool hasIndex = false;
                    bool hasKey = false;

                    foreach (Attribute attribute in property.GetCustomAttributes(false))
                    {
                        if (attribute is SqlLengthAttribute) hasLength = true;
                        if (attribute is IIndexAttribute) hasIndex = true;
                        if (attribute is IPrimaryKeyAttribute) hasKey = true;
                    }

                    if (hasKey && hasIndex && !hasLength) throw new PersistenceException(string.Format("Invalid key property. Type {0} has key property {1} defined as type string without a SqlLength attribute.", type.FullName, property.Name));
                }
            }

            //Validate normal properties
            foreach (PropertyInfo property in propertyInfo)
            {
                //Check indexed strings without length
                if (property.PropertyType == typeof(string))
                {
                    bool hasLength = false;
                    bool hasIndex = false;

                    foreach (Attribute attribute in property.GetCustomAttributes(false))
                    {
                        if (attribute is SqlLengthAttribute) hasLength = true;
                        if (attribute is IIndexAttribute) hasIndex = true;
                    }

                    if (hasIndex && !hasLength) throw new PersistenceException(string.Format("Invalid property. Type {0} has property {1} defined with an index attribute and without a SqlLength attribute.",type.FullName, property.Name)); 
                }

                //Check binary properties cannot have indexes
                if (property.PropertyType == typeof(Stream))
                {
                    bool hasIndex = false;

                    foreach (Attribute attribute in property.GetCustomAttributes(false))
                    {
                        if (attribute is IIndexAttribute) hasIndex = true;
                    }

                    if (hasIndex) throw new PersistenceException(string.Format("Invalid property. Type {0} has property {1} defined with a binary attribute which cannot be indexed.", type.FullName, property.Name));
                }
            }

        }

        //Loads primary or compound key information recursively
        protected override void GetKeyProperties(Type type)
        {
            base.GetKeyProperties(type);

            //Get only the properties for this type.
            PropertyInfo[] propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.GetProperty);

            //Get post recursive properties (attributes override those in lower classes)
            //All keys should be checked here
            foreach (PropertyInfo prop in propertyInfo)
            {
                //Loop through attributes and get settings
                object[] attributes = prop.GetCustomAttributes(false);
                foreach (Attribute attr in attributes)
                {
                    if (attr is ColumnAttribute)
                    {
                        if (attr is SqlIdentityAttribute)
                        {
                            //Check if an identity type 
                            SqlIdentityAttribute key = (SqlIdentityAttribute) attr;
                            if (key.Increment < 1) throw new PersistenceException(string.Format("Identity Increment must be greater than zero for type {0}.",type.FullName));
                            if (key.Start < 0) throw new PersistenceException(string.Format("Identity start cannot be less than zero for type {0}.",type.FullName));
                            IsIdentity = true;
                            Identity = prop;
                        }
                    }
                }
            }
        }
    }
}
